#include "util/Shared_ptr.h"
#include <assert.h>
#include <iostream>



unsigned int calledDtor = 0;
unsigned int calledCtor = 0;
unsigned int calledPrint = 0;



class TestStarter
{
public:
	TestStarter( void (*Functor)(void) )
	{
		Functor();
	}
};

class ITest
{
public:
	virtual void Print() = 0;
	virtual ~ITest(){}
};

class SharedPtrTest : public ITest
{
public:
	SharedPtrTest() : testFloat( .445f ) { ++calledCtor; }
	void Print() { ++calledPrint; }
	virtual ~SharedPtrTest(){ ++calledDtor; }

	float testFloat;
};

class SharedPtrTest2 : public SharedPtrTest
{
public:
	SharedPtrTest2() : SharedPtrTest(), testUInt( 200 ) {}

	unsigned int testUInt;
};

void TestingSharedPtr()
{
	
	{
		Boon::Weak_ptr<SharedPtrTest> wempty;
		assert( wempty.Empty() == true );
		{
			// shared::ctor( T* )
			Boon::Shared_ptr<SharedPtrTest> tmp( new SharedPtrTest() );
			assert( tmp.Get() );
			{
				// shared::ctor( shared )
				Boon::Shared_ptr<SharedPtrTest> sharedToShared( tmp );
				assert( sharedToShared.Get() == tmp.Get() );
				// weak::ctor( shared )
				Boon::Weak_ptr<SharedPtrTest> wtmp( tmp );
				assert( wtmp.Lock().Get() == tmp.Get() );
				assert( wtmp.UseCount() == 2 );
				// weak::empty()
				assert( wtmp.Empty() == false );
				tmp.Get()->Print();
				{
					//test shared::ctor( weak )
					Boon::Shared_ptr<SharedPtrTest> weakToShared( wtmp );
					Boon::Weak_ptr<SharedPtrTest> weakToWeak( wtmp );
					assert( weakToWeak.UseCount() == 3 );
					// weak::ctor( weak )
					assert( weakToWeak.Lock().Get() == wtmp.Lock().Get() );
					assert( weakToShared.Get() == wtmp.Lock().Get() );
					assert( calledCtor == 1 );
					assert( calledPrint == 1 );
					assert( calledDtor == 0 );
					Boon::Shared_ptr<SharedPtrTest> tmp2;
					assert( calledCtor == 1 );
					assert( tmp2.Get() == nullptr );
					// operator= ( shared, shared )
					tmp2 = tmp;
					assert( tmp2.Get() == tmp.Get() );
					// operator*
					(*tmp2).Print();
					assert( calledPrint == 2 );
					wempty = tmp2;
					// check operator=(weak, shared)
					assert( wempty.Lock().Get() == tmp2.Get() );
					assert( wempty.Expired() == false );
					// operator ->
					wempty.Lock()->Print();
					assert( calledPrint == 3 );
					Boon::Shared_ptr<SharedPtrTest> tmp3( new SharedPtrTest() );
					wtmp = tmp3;
					assert( wtmp.UseCount() == 1 );
					assert( wempty.UseCount() == 4 );
				}
				assert( wtmp.UseCount() == 0 );
				// weak::expired()
				assert( wtmp.Expired() == true );
				wtmp = wempty;
				assert( wempty.Expired() == false );
				assert( wtmp.UseCount() == 2 );
				assert( wempty.UseCount() == 2 );
				// check operator=( weak, weak )
				assert( wtmp.Lock().Get() == wempty.Lock().Get() );
				//weak::reset()
				wempty.Reset();
				assert( wempty.Empty() == true );
				assert( calledDtor == 1 );
				tmp->Print();
				assert( calledPrint == 4 );
			}
			wempty = tmp;
			assert( wempty.UseCount() == 1 );
		}
		assert( wempty.Expired() == true );
		assert( calledDtor == 2 );
		assert( wempty.UseCount() == 0 );
	}
	// expiring test
	{
		Boon::Weak_ptr<SharedPtrTest> w1;
		assert( w1.Empty() == true );
#ifndef LE_BOOST_SHARED_PTR
		assert( w1.Expired() == false );
#endif
		{
			Boon::Shared_ptr<SharedPtrTest> s1( new SharedPtrTest() );
			Boon::Weak_ptr<SharedPtrTest> w2( s1 );
			assert( w2.Expired() == false );
			assert( w2.Empty() == false );
			{
				Boon::Shared_ptr<SharedPtrTest> s2( new SharedPtrTest() );
				w1 = s2;
				assert( w1.Expired() == false );
				assert( w1.Empty() == false );
				w2 = w1;
				assert( w2.Expired() == false );
				assert( w2.Empty() == false );
			}
			assert( w1.Expired() == true );
			#ifndef LE_BOOST_SHARED_PTR
			assert( w1.Empty() == true );
			assert( w2.Empty() == true );
			#endif
			assert( w2.Expired() == true );
			w2 = s1;
			w1 = w2;
		}
		#ifndef LE_BOOST_SHARED_PTR
		assert( w1.Empty() == true );
		#endif
		assert( w1.Expired() == true );
	}
	{
		calledDtor = 0;
		Boon::Shared_ptr<ITest>bla( new SharedPtrTest2());
		bla->Print();
	}
	assert( calledDtor == 1 );

	// convert to base
	// test template ctors shared, weak
	Boon::Shared_ptr<SharedPtrTest2> derived( new SharedPtrTest2() );
	assert( derived->testFloat == .445f );
	assert( derived->testUInt == 200 );
	Boon::Shared_ptr<SharedPtrTest> base( derived );
	assert( derived.Get() == base.Get() );
	assert( base->testFloat == .445f );
	// converstion weak::ctor( shared )
	Boon::Weak_ptr<SharedPtrTest> wbase( derived );
	assert( derived.Get() == wbase.Lock().Get() );
	assert( wbase.Lock()->testFloat == .445f );
	// converstion weak::ctor( weak )
	Boon::Weak_ptr<SharedPtrTest2> base2( wbase );
	assert( base2.Lock().Get() == wbase.Lock().Get() );
	assert( base->testFloat == .445f );
	// conversion shared::ctor( weak )
	Boon::Shared_ptr<SharedPtrTest> derived2( base2 );
	assert( derived2.Get() == base2.Lock().Get() );
	assert( derived2->testFloat == .445f );
	assert( base2.Lock()->testUInt == 200 );
	std::cout << "Test Shared_ptr: OK" << std::endl;
}

#ifdef RUN_TESTING
TestStarter startTest( &TestingSharedPtr );
#endif
